package com.xj.anylogic;

import java.io.BufferedReader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.HashMap;

import org.apache.log4j.Logger;

import edu.cs.usask.ca.silver.definitions.SystemProperties;

/**
 * all paths are set relatively to running command in workDir (./tmp). TODO use
 * System.properties for file dilemeter
 * 
 * @author Yudi Xue
 * 
 */
public class AlpSim extends Thread {

	/**
	 * command saves all commands.
	 */
	public final String command;
	public final String weaverSnapshotPath;
	public final String weavergcollectPath;
	public final String aspectjrtPath;
	public final String aspectjweaverPath;
	private static Logger logger = Logger.getLogger(AlpSim.class);

	/**
	 * test.
	 * 
	 * @param command
	 * @throws IOException
	 */
	private AlpSim(final String command) throws IOException, URISyntaxException {
		this.command = command;

		File gcollectJar = readFile(this.getClass().getClassLoader()
				.getResource("weaver_with_google_collection.jar").openStream(),
				"weaver_with_google_collection", ".jar");
		File snapshotJar = readFile(this.getClass().getClassLoader()
				.getResource("weaver_snapshot_mac.jar").openStream(),
				"weaver_snapshot_mac", ".jar");
		File aspectjrtJar = readFile(this.getClass().getClassLoader()
				.getResource("aspectjrt.jar").openStream(), "aspectjrt", ".jar");
		
		File aspectjweaverJar = readFile(this.getClass().getClassLoader()
				.getResource("aspectjweaver.jar").openStream(), "aspectjweaver", ".jar");

		aspectjrtPath = aspectjrtJar.getAbsolutePath();
		weaverSnapshotPath = snapshotJar.getAbsolutePath();
		weavergcollectPath = gcollectJar.getAbsolutePath();
		aspectjweaverPath = aspectjweaverJar.getAbsolutePath();
		logger.info("aspectjrt: " + aspectjrtPath);

	}

	/**
	 * Returns a jar file that is read in from a stream
	 * 
	 * @param sFilename
	 * @return
	 * @throws java.io.IOException
	 */
	private static File readFile(InputStream sFilename, String prefix,
			String suffix) throws IOException {

		InputStream in = sFilename;
		// ByteArrayOutputStream out = new ByteArrayOutputStream(1024);

		File outputFile = File.createTempFile(prefix, suffix);
		FileOutputStream fos = new FileOutputStream(outputFile);

		byte[] aoBuffer = new byte[512];

		int nBytesRead;

		while ((nBytesRead = in.read(aoBuffer)) > 0) {
			fos.write(aoBuffer, 0, nBytesRead);
		}

		return outputFile;
	}

	/**
	 * Search for jarfile Path specifically to the platform, this method is
	 * designed specifically to handle Anylogic 6.2.2 structure.
	 * 
	 * TODO: throw exception
	 * 
	 * @param anylogicPath
	 * @return HashMap<String, String> which contains absolute path to anylogic
	 *         jar files.
	 */
	private static HashMap<String, String> jarPathSixTwo(String anylogicPath) {

		// append the file separator to the path.
		if (!anylogicPath.endsWith(File.separator)) {
			anylogicPath = anylogicPath.concat(File.separator);
		}

		HashMap<String, String> map = new HashMap<String, String>();

			// should be /Applications/AnyLogic/plugins/
			String anylogicPluginPath = anylogicPath.concat("plugins"
					+ File.separator);
			File anylogicPlugin = new File(anylogicPluginPath);
			logger.info("AnyLogic plugin is at: "
					+ anylogicPlugin.getAbsolutePath());
			String[] directory = anylogicPlugin.list();

			for (int i = 0; i < directory.length; i++) {
				if (directory[i].contains("com.xj.anylogic.engine")) {
					map.put("xjanylogic6engine.jar", anylogicPluginPath.concat(
							directory[i]).concat(
							File.separator + "xjanylogic6engine.jar"));
					logger.info("found xjanylogic6engine.jar at: "
							+ anylogicPluginPath.concat(directory[i]).concat(
									"xjanylogic6engine.jar"));
				}
				// no regular expression required.
				if (directory[i].contains("com.xj.anylogic.core")) {
					map
							.put(
									"com.xj.anylogic.core.jar",
									anylogicPluginPath
											.concat(directory[i])
											.concat(
													File.separator
															+ "com.xj.anylogic.core.jar"));
					map.put("com.xj.anylogic.ms.jar", anylogicPluginPath
							.concat(directory[i]).concat(
									File.separator + "com.xj.anylogic.ms.jar"));
					logger.info("found com.xj.anylogic.core.jar at: "
							+ anylogicPluginPath.concat(directory[i]).concat(
									"com.xj.anylogic.core.jar"));
					logger.info("found com.xj.anylogic.ms.jar at "
							+ anylogicPluginPath.concat(directory[i]).concat(
									"com.xj.anylogic.ms.jar"));
				}
				if (directory[i].contains("com.xj.anylogic.optimizer")) {
					map.put("OptQuest.jar", anylogicPluginPath.concat(
							directory[i]).concat(
							File.separator + "OptQuest.jar"));
				}
			}
			return map;
	
	}

	/**
	 * Static factory to execute simulation without any third-party operation.
	 * This method is for running SILVER under Mac OS X.
	 * 
	 * 
	 * @param packageName
	 *            - package name refers to the package name of the simulation
	 * @param jarfilePath
	 *            - ABSOLUTE PATH of the jarfile packed up with simulation class
	 *            files produced by Anylogic
	 * @return AlpSim instance with state variable logging ability supported
	 *         with Google Collection Library RC2
	 * @throws IOException
	 *             TODO: check file existence (dependency integraty)
	 */
	public static AlpSim alpSim(final String packageName,
			final String jarfilePath, final String anylogicPath)
			throws IOException, URISyntaxException {
		/*
		 * Original command String gcommand = "java -Xmx1200m -javaagent:"+agent
		 * + " -classpath " + ms + ":" + optQuest + ":" + engine + ":" +
		 * jarfile.getAbsolutePath() + ":" + aj + ":" + gweaver + ":" + csvlib +
		 * ":" + gcollect + ":" + " com.xj.anylogic.ms.MS " + target;
		 */

		HashMap<String, String> map = jarPathSixTwo(anylogicPath);
		
		String commandForBasicSim;
		if(SystemProperties.OS_NAME.startsWith("Windows")){
			commandForBasicSim = "java -Xmx500m -classpath " + "\""
				+ map.get("com.xj.anylogic.ms.jar") + File.pathSeparator
				+ map.get("com.xj.anylogic.core.jar") + File.pathSeparator
				+ map.get("OptQuest.jar") + File.pathSeparator
				+ map.get("xjanylogic6engine.jar") + File.pathSeparator
				+ jarfilePath + File.pathSeparator 
				+ "\""
				+ " com.xj.anylogic.ms.MS "
				+ packageName + ".Simulation";
		} else {

		commandForBasicSim = "java -Xmx500m -classpath "
				+ map.get("com.xj.anylogic.ms.jar") + File.pathSeparator
				+ map.get("com.xj.anylogic.core.jar") + File.pathSeparator
				+ map.get("OptQuest.jar") + File.pathSeparator
				+ map.get("xjanylogic6engine.jar") + File.pathSeparator
				+ jarfilePath + File.pathSeparator + " com.xj.anylogic.ms.MS "
				+ packageName + ".Simulation";
		}
		return new AlpSim(commandForBasicSim);
	}

	/**
	 * Static factory method to create AlpSim with screenshot aspectJ. This
	 * method is for running SILVER under Mac OS X.
	 * 
	 * 
	 * @param packageName
	 *            - package name refers to the package name of the simulation
	 * @param jarfilePath
	 *            - ABSOLUTE PATH of the jarfile packed up with simulation class
	 *            files produced by Anylogic
	 * @return AlpSim instance with screenshot ability
	 * @throws IOException
	 *             TODO: check file existence (dependency integraty)
	 */
	public static AlpSim alpSimWithScreenShotOSX(final String packageName,
			final String jarfilePath, final String anylogicPath)
			throws IOException, URISyntaxException {

		HashMap<String, String> map = jarPathSixTwo(anylogicPath);

		AlpSim temp = new AlpSim("");
		
		String commandForScreenShot;
		
		if(SystemProperties.OS_NAME.startsWith("Windows")){
			commandForScreenShot = "java -Xmx500m -javaagent:"
				+ temp.aspectjweaverPath
				+ " -classpath \""
				+ map.get("com.xj.anylogic.ms.jar") + File.pathSeparator
				+ map.get("com.xj.anylogic.core.jar") + File.pathSeparator
				+ map.get("OptQuest.jar") + File.pathSeparator
				+ map.get("xjanylogic6engine.jar") + File.pathSeparator
				+ jarfilePath + File.pathSeparator 
				+ File.pathSeparator + temp.aspectjrtPath
				+ File.pathSeparator + temp.weaverSnapshotPath
				+ "\""
				+ " com.xj.anylogic.ms.MS "
				+ packageName + ".Simulation";
		} else {
			commandForScreenShot = "java -Xmx500m -javaagent:"
				+ temp.aspectjweaverPath + " -classpath " + map.get("com.xj.anylogic.ms.jar")
				+ File.pathSeparator + map.get("com.xj.anylogic.core.jar")
				+ File.pathSeparator + map.get("OptQuest.jar")
				+ File.pathSeparator + map.get("xjanylogic6engine.jar")
				+ File.pathSeparator + temp.aspectjrtPath
				+ File.pathSeparator + temp.weaverSnapshotPath
				+ File.pathSeparator + jarfilePath + File.pathSeparator
				+ " com.xj.anylogic.ms.MS " + packageName + ".Simulation";
		}
		return new AlpSim(commandForScreenShot);
	}

	/**
	 * Static factory method to create AlpSim with state variable logging
	 * aspectJ. This method is for running SILVER under Mac OS X.
	 * 
	 * 
	 * @param packageName
	 *            - package name refers to the package name of the simulation
	 * @param jarfilePath
	 *            - ABSOLUTE PATH of the jarfile packed up with simulation class
	 *            files produced by Anylogic
	 * @return AlpSim instance with state variable logging ability supported
	 *         with Google Collection Library RC2
	 * @throws IOException
	 *             TODO: check file existence (dependency integraty)
	 */
	public static AlpSim alpSimWithLoggingOSX(final String packageName,
			final String jarfilePath, final String anylogicPath)
			throws IOException, URISyntaxException {
		/*
		 * Original command String gcommand = "java -Xmx1200m -javaagent:"+agent
		 * + " -classpath " + ms + ":" + optQuest + ":" + engine + ":" +
		 * jarfile.getAbsolutePath() + ":" + aj + ":" + gweaver + ":" + csvlib +
		 * ":" + gcollect + ":" + " com.xj.anylogic.ms.MS " + target;
		 */
		HashMap<String, String> map = jarPathSixTwo(anylogicPath);

		AlpSim temp = new AlpSim("");

		String commandForLogging = "java -Xmx500m -javaagent:"
				+ temp.aspectjweaverPath + " -classpath " + map.get("com.xj.anylogic.ms.jar")
				+ File.pathSeparator + map.get("com.xj.anylogic.core.jar")
				+ File.pathSeparator + map.get("OptQuest.jar")
				+ File.pathSeparator + map.get("xjanylogic6engine.jar")
				+ File.pathSeparator + temp.aspectjrtPath
				+ File.pathSeparator + temp.weaverSnapshotPath
				+ File.pathSeparator + jarfilePath + File.pathSeparator
				+ " com.xj.anylogic.ms.MS " + packageName + ".Simulation";

		return new AlpSim(commandForLogging);
	}

	/**
	 * This is the output printing.
	 * 
	 * @param ps
	 *            - a process
	 */
	private static void printOutput(final Process ps) {
		try {
			InputStream is = ps.getErrorStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			String line;
			while ((line = br.readLine()) != null) {
				logger.info(line);
			}
			is = ps.getInputStream();
			isr = new InputStreamReader(is);
			br = new BufferedReader(isr);
			line = null;
			while ((line = br.readLine()) != null) {
				logger.info(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void start() {

		Runtime rt = Runtime.getRuntime();
		File workDir = new File("tmp");
		workDir.mkdir();

		try {
			Process ps = rt.exec(command, null, workDir);
			logger.info(command);
			printOutput(ps);

			if (ps.waitFor() == 0) {
				File[] list = workDir.listFiles();
				for (int i = 0; i < list.length; i++) {
					if (list[i].getName().endsWith(".txt"))
						list[i].delete();
				}

			}
			logger.info("Process exitValue: " + ps.waitFor());

		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

}
